import { FileItem, CheckPoints, SystemStats, Model, Prompt, ImageUploadResult, NodeInfo, Queue } from "./type";
import { v4 } from "uuid";
import { createRequest } from "./utils";
import { EventEmitter } from 'events';

export type ClientInstance = {
  wsInstance: WebSocket;
  on: (type: string, callback: (data: any) => void) => void;
  off: (type: string, callback?: (data: any) => void) => void;
  once: (type: string, callback: (data: any) => void) => void;
  createPrompt: (prompt: Prompt) => Promise<{
    promptId: string,
    number: number,
    nodeErrors: any,
  }>;
  getPrompt: (promptId: string) => Promise<Prompt>;
  deletePrompt: (promptId: string) => Promise<void>;
  getCheckPoints: () => Promise<CheckPoints[]>;
  getExtensions: () => Promise<FileItem[]>;
  getModels: () => Promise<Model[]>;
  getLoras: () => Promise<FileItem[]>;
  getEmbeddings: () => Promise<string[]>;
  getSystemStats: () => Promise<SystemStats>;
  getHistory: (promptId: string) => Promise<any>;
  getHistories: (maxItem: number) => Promise<FileItem[]>;
  getQueue: () => Promise<any>;
  uploadImage: (file: File) => Promise<ImageUploadResult>;
  uploadMask: (file: File) => Promise<ImageUploadResult>;
  interrupt: () => Promise<void>;
  clearQueue: () => Promise<void>;
  deleteQueue: (promptId: string) => Promise<void>;
  getQueueCount: () => Promise<number>;
  getObjectInfo: () => Promise<Record<string, NodeInfo>>;
  getNodeInfo: (nodeClass: string) => Promise<NodeInfo>;
  viewMetadata: (folderName: string) => Promise<any>;
};

type CreateClientPrams = {
  baseUrl?: string;
  clientId?: string;
  disableWs?: true;
};

export const createClient = (params: CreateClientPrams = {}) => {
  const { baseUrl = "127.0.0.1:8188", clientId = v4(), disableWs= false } = params;
  const { get, post, postForm } = createRequest(`http://${baseUrl}/api`);
  
  // 初始化WebSocket连接
  const wsUrl = `ws://${baseUrl}/ws?clientId=${clientId}`;
  let wsInstance: WebSocket | null = null
  let eventEmitter: EventEmitter

  if (!disableWs) {
    wsInstance = new WebSocket(wsUrl);
    eventEmitter = new EventEmitter();

    wsInstance.onmessage = (event) => {
      if (typeof event.data !== 'string') {
        return;
      }
      try {
        const message = JSON.parse(event.data);
        if (message && message.type) {
          eventEmitter.emit(message.type, message.data);
        }
      } catch (e) {
        console.error('Failed to parse WebSocket message', e);
      }

    };
  }


  return {
    on: (type: string, callback: (data: any) => void) => eventEmitter.on(type, callback),
    off: (type: string, callback: (data: any) => void) => callback ? eventEmitter.off(type, callback) : eventEmitter.removeAllListeners(type),
    once: (type: string, callback: (data: any) => void) => eventEmitter.once(type, callback),
    wsInstance,
    createPrompt: (prompt: Prompt) => 
      post<{
        promptId: string,
        number: number,
        nodeErrors: any,
      }>('/prompt', {}, {clientId, prompt}),
    getPrompt: (promptId: string) => 
      get<Prompt>(`/prompt/${promptId}`),
    deletePrompt: (promptId: string) =>
      post<void>(`/prompt/${promptId}/delete`),
    getCheckPoints: () => get<FileItem[]>("/experiment/models/checkpoints"),
    getExtensions: () => get<FileItem[]>("/experiment/models/checkpoints"),
    getModels: () => get<Model[]>("/models"),
    getLoras: () => get<FileItem[]>("/loras"),
    getEmbeddings: () => get<string[]>("/embeddings"),
    getSystemStats: () => get<SystemStats>("/system/stats"),
    getHistory: (promptId: string): Promise<Record<string, any>> => get(`/history/${promptId}`),
    getHistories: (maxItem: number) => get<FileItem[]>("/history", { limit: maxItem }),
    uploadImage: (formData: FormData) => postForm<ImageUploadResult>("/upload/image", formData),
    uploadMask: (formData: FormData) => postForm<ImageUploadResult>("/upload/mask", formData),
    interrupt: () => post<void>("/interrupt"),
    clearQueue: () => post<void>("/queue"),
    deleteQueue: (promptId: string) => post<void>(`/queue`, {}, { delete: promptId}),
    getQueue: () => get<Queue>("/queue"),
    getObjectInfo: () => get<Record<string, NodeInfo>>("/object_info"),
    getNodeInfo: (nodeClass: string) => get<NodeInfo>(`/object_info/${nodeClass}`),
    viewMetadata: (folderName: string) => get<any>(`/view_metadata/${folderName}`),
  };
};
